// 初始化，渲染初始的代码，汇编代码，寄存器，pc，内存
let renderer = {
    asm() {
        // 辅助函数，获取html
        let frag = document.createDocumentFragment()
        for (let i = 0; i < asm.length; i++) {
            let it = asm[i]
            let dom = getMemoryItem(it.add, it.asmCode)
            dom.dataset.i = i
            frag.appendChild(dom)
        }
        $('.asm .memory').appendChild(frag)
    },
    regs() {
        let keys = Object.keys(regs)
        let frag = document.createDocumentFragment()
        for (const it of keys) {
            frag.appendChild(getMemoryItem(it, regs[it]))
        }
        // 加上pc
        frag.appendChild(getMemoryItem('pc', pc.get()))
        $('.regs .memory').appendChild(frag)
    },
    stack() {
        let frag = document.createDocumentFragment()
        for (let i = 0; i < stack.length; i++) {
            let it = stack[i]
            let dom = getMemoryItem(it[0], it[1])
            dom.dataset.i = i
            frag.appendChild(dom)
        }
        $('.stack .memory').appendChild(frag)
    },
    cCodes() {
        function _getCodeLineDom(val, i) {
            let dom = document.createElement('DIV')
            let lineNum = document.createElement('DIV')
            let code = document.createElement('DIV')
            dom.className = `code-line line-${i}`
            lineNum.textContent = i
            lineNum.className = 'line-number'
            if (val[0] === '\t') {
                code.classList.add('indent-1')
            }
            code.textContent = val
            dom.appendChild(lineNum)
            dom.appendChild(code)
            return dom
        }
        let frag = document.createDocumentFragment()
        for (let i = 0; i < cCodes.length; i++) {
            let it = cCodes[i]
            let dom = _getCodeLineDom(it.code, it.add)
            dom.dataset.i = i
            frag.appendChild(dom)
        }
        $('.c-code').appendChild(frag)
    },
}
// 渲染
async function initRender() {
    renderer.asm()
    renderer.regs()
    renderer.stack()
    renderer.cCodes()
    // 移动页面中的各种指针到合适的位置

    movePointer('pc', pc.get())
    await movePointer('esp', regs.esp)
}

// 事件注册

/**
 * 执行下一步
 */
const handleNext = async () => {
    if (globalConfig.done) {
        return 'done'
    }
    if (!globalConfig.running) {
        globalConfig.running = true
        return 'unrunning'
    }
    if (!globalConfig.clickable) {
        return
    }
    globalConfig.clickable = false
    let i = pc.getIndex()
    let res = ''
    if (asm[i].anime) {
        res = await asm[i].anime()
    }
    if (res === 'done') {
        globalConfig.done = true
        $('.btn-pause').classList.add('hide')
        $('.btn-next').classList.add('hide')
        $('.btn-one-step').classList.add('hide')
        return 'done'
    }
    globalConfig.clickable = true
}

$('.btn-next').onclick = handleNext
document.addEventListener('keydown', (e) => {
    if (e.key === 'n') {
        handleNext()
    }
})
// 执行到末尾时不断点击下一步

$('.btn-one-step').onclick = async () => {
    globalConfig.running = true
    // 显示暂停按钮
    $('.btn-one-step').classList.add('hide')
    $('.btn-next').classList.add('hide')
    $('.btn-pause').classList.remove('hide')
    let res = ''
    while (res !== 'done' && res !== 'unrunning') {
        res = await handleNext()
    }
    // globalConfig.running = false
}

$('.btn-pause').onclick = async () => {
    $('.btn-pause').classList.add('hide')
    $('.btn-one-step').classList.remove('hide')
    $('.btn-next').classList.remove('hide')
    globalConfig.running = false
}

$('.ipt-an').addEventListener('change', (e) => {
    globalConfig.an = +e.target.value
    initAll()
    // console.log(e.target.value)
})

$('.select-speed').addEventListener('change', (e) => {
    globalConfig.speed = +e.target.value
})
$('.select-speed').value = globalConfig.speed

$('.chbox-minimal').addEventListener('change', (e) => {
    checkoutMinimal(e.target.checked)
    // console.log(e.target.checked)
    // globalConfig.speed = +e.target.value
})

$('.btn-reload').onclick = () => {
    $('.btn-reload').classList.add('hide')
    $('.btn-one-step').classList.remove('hide')
    $('.btn-next').classList.remove('hide')
    initAll()
}

function checkoutMinimal(val) {
    if ($('.chbox-minimal').checked !== val) {
        $('.chbox-minimal').checked = val
    }
    globalConfig.minimal = val
    if (val) {
        $('.asm').classList.add('minimal')
        $('.stack').classList.add('minimal')
    } else {
        $('.asm').classList.remove('minimal')
        $('.stack').classList.remove('minimal')
    }
}

/**
 * 初始化数据
 */
const initAll = async () => {
    globalConfig.running = false
    globalConfig.done = true
    let speed = globalConfig.speed
    globalConfig.speed = 0

    regs.esp = globalConfig.initialEsp
    regs.ebp = globalConfig.initialEsp + 40

    fibonacci.initial = globalConfig.an
    fibonacci.paramStack = []
    fibonacci.cmpl = undefined

    stack = []

    initStack()

    // 删除页面中的所有memory-item
    for (const it of $('.memory-item')) {
        it.remove()
    }
    for (const it of $('.code-line')) {
        it.remove()
    }

    // 更新asm
    asm.findIndex((v) => {
        if (v.add === 0x804844a) {
            v.asmCode = `movl   $${getHex(globalConfig.an)},(%esp)`
            v.text = `movl   $${getHex(
                globalConfig.an
            )},(%esp) 指令会把数字${getHex(globalConfig.an)}放到内存中(%esp)处`
            return true
        } else {
            return false
        }
    })
    // 更新c-code
    cCodes[7].code = `\tint i = f(${globalConfig.an})`

    await initRender()

    await Promise.all([
        movePointer('esp', regs.esp),
        movePointer('ebp', regs.ebp),
        pc.set(0x804844a),
        cCodeAction.setPC(7),
    ])

    $('.btn-pause').classList.add('hide')
    $('.btn-one-step').classList.remove('hide')
    $('.btn-next').classList.remove('hide')
    $('.btn-reload').classList.add('hide')

    setOutput('输出结果：')

    treeConfig.n = globalConfig.an
    $('.ipt-an').value = globalConfig.an
    initTree()

    checkoutMinimal(globalConfig.minimal)

    globalConfig.done = false
    globalConfig.running = true
    globalConfig.clickable = true
    globalConfig.speed = speed
}

initAll()
